import time,datetime
import redis
import json
import requests
import hashlib
from bs4 import BeautifulSoup
from elasticsearch import Elasticsearch
from loguru import logger
from minio import Minio
from io import BytesIO
import re


class WxGzhCaiji:
    # 初始化
    def __init__(self):
        # 配置Redis连接参数
        redis_host = "192.168.1.112"  # Redis服务器地址
        redis_port = 6379  # Redis端口（默认6379）
        redis_db = 15  # 数据库编号（默认0）
        # 创建Redis连接对象（带密码验证）
        self.r = redis.Redis(
            host=redis_host,
            port=redis_port,
            password='qqqAAA0130',
            db=redis_db,
            decode_responses=True  # 自动将返回结果解码为字符串（可选）
        )
        # 创建ES连接对象
        self.es = Elasticsearch(
            hosts=['http://192.168.1.92:9200'],
            basic_auth=('elastic', 'qqqAAA0130'),
            request_timeout=3600
        )

        # MinIO服务器配置
        endpoint = "192.168.1.104:9002"  # 替换为你的MinIO地址
        access_key = "H53CPN3BAVPTBUTDVW33"  # 替换为你的Access Key
        secret_key = "KzgUpTuVjtzdnLBD35DxoZF99MexmT9PNgW23n0A"  # 替换为你的Secret Key
        secure = False  # 如果使用HTTPS则设为True
        # 创建Minio连接对象
        self.client = Minio(
            endpoint=endpoint,
            access_key=access_key,
            secret_key=secret_key,
            secure=secure
        )
    def get_image(self,url,bucket_name,img_dir) :
        try:
            bucket_name = bucket_name.replace("_", "-").strip()
            # 检查并创建存储桶（如果不存在）
            found = self.client.bucket_exists(bucket_name)
            if not found:
                self.client.make_bucket(bucket_name)
                logger.info(f"存储桶 '{bucket_name}' 创建成功")
            else:
                logger.info(f"存储桶 '{bucket_name}' 已存在")

            # 获取图片内容
            res = requests.get(url)
            # 生成图片名称
            img_name = f'{img_dir}/{hashlib.md5(res.content).hexdigest()}.jpeg'

            # 上传图片
            self.client.put_object(
                bucket_name=bucket_name,
                object_name=img_name,
                data=BytesIO(res.content),
                length=len(res.content)
            )
            logger.info(f"图片 {img_name} 保存成功")
            return True, f"http://oss.nei.47466.com:9002/{bucket_name}/{img_name}"
        except Exception as e:
            logger.error(f"意外错误: - {e}")
            return False,""


    def reptile(self,link):
        url = link
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0',
        }
        # 创建爬虫对象
        requst = requests.get(url=url, headers=headers, allow_redirects=True)
        # 格式化响应数据
        html = requst.text
        soup = BeautifulSoup(html, 'lxml')  # 使用 lxml 解析器
        # 使用bs4通过id匹配div
        div = soup.find('div', id='js_content')
        try:
            if 'style' in div.attrs:
                del div['style']
        except Exception as e:
            logger.error(f"错误 {div} {e}!!!")
            return ""
        return div
    def write_in_data(self,name,title,save_img_num,article_index,es_doc_id,url,img_dir,minio_bucket,config):
        try:
            # 通过 id 判断当前文章是否已经入库
            if self.es.exists(index=article_index, id=es_doc_id):
                logger.warning(f"【{name}】 文章 {title} 已经入库不用重新入库")
            else:
                data = self.reptile(url)
                if data == "":
                    logger.error(f"【{name}】 获取文章 {title} 内容失败")
                    pass
                img_list = data.select('img')
                # 删除每个img标签
                for index, img in enumerate(img_list):
                    # 使用decompose()方法完全移除标签
                    if index >= save_img_num:
                        img.decompose()
                    else:
                        # 获取图片地址
                        if img.has_attr('data-src'):
                            img_arr = img.attrs.get('data-src')
                        elif img.has_attr('src'):
                            img_arr = img.attrs.get('src')
                        else:
                            img_arr = ''
                        if img_dir == '':
                            custom_str = datetime.datetime.now().strftime("%Y/%m/%d")
                            img_dir = f'default/{custom_str}'
                        # 保存图片
                        ok, img_url = self.get_image(img_arr, minio_bucket, img_dir)
                        if ok:
                            # 设置img src属性
                            img.attrs['src'] = img_url
                            logger.info(f"图片 {img_arr} 的路径设置成功")
                        else:
                            img.decompose()
                        if img.has_attr('data-src'):
                            del img['data-src']
                        else:
                            logger.warning(f"{img} 没有 data-src 属性")
                data_str = str(data)
                # 匹配任何中文字符（包括基本汉字、扩展A/B区等）
                pattern = re.compile(r'[\u4e00-\u9fff\u3400-\u4dbf\U00020000-\U0002a6df]')
                if pattern.search(data_str):
                    doc = {
                              "@timestamp": datetime.datetime.now().isoformat() + "+0800",
                              "title": title,
                              "content": str(data),
                              "source_url": url,
                              "gzh_name": name,
                              "pubnum": 0,
                          } | config['other_map']
                    self.es.index(index=article_index, id=es_doc_id, body=doc)
                    logger.info(f"【{name}】 文章 {title} 入库成功")
                else:
                    logger.warning(f"【{name}】 获取文章 {title} 失败,里面没有中文，请检查！")
        except Exception as e:
            logger.error(f"意外错误: - {e}")


    # 1. 一直运行，从Redis读取数据，如果有，运行，如果没有，等待
    def Extractdata(self):
        while True:
            # 获取所有公众号入库配置
            try:
                key_pattern = "wx_caiji:*:config"
                config_keys: list = self.r.keys(key_pattern)
                logger.info(f'获取所有的公众号信息{config_keys}')
                for config_key in config_keys:
                    logger.info(f'现在是{config_key}的爬取时间')
                    config = json.loads(self.r.get(config_key))
                    is_once = config.get('once', False)
                    name = config['name']
                    article_index = config['es_index'].strip()
                    save_img_num = config['save_img_num']
                    minio_bucket = config['minio_bucket']
                    img_dir = config['img_dir']
                    if is_once:
                        logger.info('一次性采集所有文章')
                        # 一次性采集所有文章，
                        article_key = f"wx_caiji:{name}:article_list"
                        while True:
                            article = self.r.lpop(article_key)
                            if not article:
                                break
                            # 一篇文章
                            article = json.loads(article)
                            aid = article['aid']
                            # 遍历文章列表
                            url = article['link']
                            key = f"{name}:{aid}"
                            es_doc_id = f'{hashlib.md5(key.encode()).hexdigest()}'
                            title = article['title']
                            # 写入数据
                            self.write_in_data(name,title,save_img_num,article_index,es_doc_id,url,img_dir,minio_bucket,config)
                    else:
                        #数据库的索引
                        article_key = f"wx_caiji:{name}:list"
                        # 今天日期
                        cur = datetime.datetime.now()
                        # 获取昨天的日期
                        yerday = datetime.datetime.now() - datetime.timedelta(days=1)
                        #格式化日期转换成时间戳
                        cur_time = time.mktime(cur.timetuple())
                        yerday_time = time.mktime(yerday.timetuple())
                        # 获取文章列表
                        article_ids = self.r.zrangebyscore(article_key, yerday_time, cur_time)
                        if len(article_ids) == 0:
                            logger.info(f"【{name}】最近一天没有更新文章，跳过！")
                            continue
                        logger.info(f"【{name}】 最近一天文章数量：{len(article_ids)}")
                        # 遍历文章列表
                        for article_id in article_ids:
                            article_key = f"wx_caiji:{name}:article:{article_id}"
                            article_index = config['es_index']
                            url = json.loads(self.r.get(article_key))['link']
                            key = f"{name}:{article_id}"
                            es_doc_id = hashlib.md5(key.encode()).hexdigest()
                            title1 = json.loads(self.r.get(article_key))
                            title = title1['title']
                            # 写入数据
                            self.write_in_data(name, title, save_img_num, article_index, es_doc_id, url, img_dir,minio_bucket,config)
                    logger.info(f"【{name}】 运行完成, 睡眠3分钟")
                    time.sleep(180)
                logger.info("所有公众号已经完成爬取")
                # 睡眠 5 分钟
                time.sleep(300)
            except Exception as e:
                logger.error(f"程序异常: {e}")

wxcj = WxGzhCaiji()
wxcj.Extractdata() 